using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Ideopuzzle
{
    public class Tile : ImgObject
    {
        #region Fields

        public static readonly float TIME_TO_MOVE = 0.3f;

        private GameBoard board;
        private Vector2 initial_position;
        private Vector2 final_position;
        private Vector2 pos_onboard;
        private Vector2 dest_pos_onboard;
        private List<Tile> similar_neighbours;

        private float timer = 0;
        private int type;
        private bool isMoving = false, isFalling = false, picked = false, exploded, refilled = true;

        #endregion

        #region Constructors

        public Tile(GameBoard board, SpriteBatch sprite_batch, Texture2D texture, Vector2 position, Vector2 pos_onboard, int type)
            : base(sprite_batch, texture, position)
        {
            this.board = board;
            this.initial_position = position;
            this.pos_onboard = pos_onboard;
            this.type = type;

            similar_neighbours = new List<Tile>(4);
        }

        public Tile(GameBoard board, SpriteBatch sprite_batch, Texture2D texture, Vector2 position, Vector2 dest_pos, Vector2 pos_onboard, int type)
            : this(board, sprite_batch, texture, position, pos_onboard, type)
        {
            DropTo(dest_pos, pos_onboard);

            board.Tiles[(int)pos_onboard.X, (int)pos_onboard.Y].refilled = true;
        }

        // Fake Constructors

        public void CreateFallingTile(Texture2D texture, Vector2 position, Vector2 dest_pos, Vector2 pos_onboard, int type)
        {
            this.initial_position = position;
            this.pos_onboard = pos_onboard;
            this.type = type;

            this.rectangle.X = (int)position.X;
            this.rectangle.Y = (int)position.Y;
            this.texture = texture;

            DropTo(dest_pos, pos_onboard);

            refilled = true; 
            exploded = false; 
            picked = false;

            similar_neighbours.Clear();
        }

        public void CreateFallingTile(Tile g)
        {
            this.initial_position = g.initial_position;
            this.final_position = g.final_position;
            this.dest_pos_onboard = g.dest_pos_onboard;
            this.pos_onboard = g.pos_onboard;
            this.type = g.type;

            this.rectangle = g.rectangle;
            this.texture = g.texture;

            //DropTo(g.final_position, g.dest_pos_onboard);

            refilled = true;
            isFalling = true;
            exploded = false;
            picked = false;
            isMoving = false;
            timer = 0;

            similar_neighbours.Clear();
        }        

        #endregion

        #region Draw

        public void DrawGem(GameTime gameTime)
        {
            if (!exploded)
            {
                if (isMoving)
                {
                    timer = Math.Min(timer + (float)gameTime.ElapsedGameTime.TotalSeconds, TIME_TO_MOVE);

                    rectangle.X = (int)MathHelper.Lerp(initial_position.X, final_position.X, timer / TIME_TO_MOVE);
                    rectangle.Y = (int)MathHelper.Lerp(initial_position.Y, final_position.Y, timer / TIME_TO_MOVE);
                }

                if (isFalling)
                {
                    timer = Math.Min(timer + (float)gameTime.ElapsedGameTime.TotalSeconds, TIME_TO_MOVE);

                    rectangle.X = (int)MathHelper.SmoothStep(initial_position.X, final_position.X, timer / TIME_TO_MOVE);
                    rectangle.Y = (int)MathHelper.SmoothStep(initial_position.Y, final_position.Y, timer / TIME_TO_MOVE);
                }

                if (timer >= TIME_TO_MOVE)
                {
                    isMoving = false;
                    isFalling = false;

                    board.Tiles[(int)dest_pos_onboard.X, (int)dest_pos_onboard.Y] = this;
                    //rectangle.X = (int)final_position.X; rectangle.Y = (int)final_position.Y;
                    pos_onboard = dest_pos_onboard;

                    refilled = true;

                    timer = 0;
                }

                DrawAfterBegin();
            }
        }

        #endregion

        public void MoveTo(Vector2 position, Vector2 dest_onboard)
        {
            dest_pos_onboard = dest_onboard;
            initial_position = new Vector2(this.rectangle.X, this.rectangle.Y);
            final_position = position;

            isMoving = true;
            refilled = false;
            timer = 0;
        }

        public void DropTo(Vector2 position, Vector2 dest_onboard)
        {
            MoveTo(position, dest_onboard);

            isMoving = false;
            refilled = false;

            isFalling = true;
        }

        public void Explode()
        {
            exploded = true;
            isMoving = false;
            isFalling = false;

            picked = false;
            refilled = false;
            timer = 0;            

            //System.Diagnostics.Debug.WriteLine("Gem [" + XCoord + "," + YCoord + "] explode");
        }

        public bool ConvertTile(Texture2D texture, int type)
        {
            if (!Moving && !exploded && refilled && !picked && this.type != type)
            {
                this.texture = texture;
                this.type = type;
                return true;
            }

            return false;
        }

        public List<Tile> GetSimilarNeighbours()
        {
            CheckNeighbours();

            return similar_neighbours;
        }

        public void CheckNeighbours()
        {
            similar_neighbours.Clear();

            // N neighbour
            if (XCoord - 1 >= 0 && !board.Tiles[XCoord - 1, YCoord].exploded && !board.Tiles[XCoord - 1, YCoord].picked && 
                !board.Tiles[XCoord - 1, YCoord].Moving && board.Tiles[XCoord - 1, YCoord].Type == type)
            {
                similar_neighbours.Add(board.Tiles[XCoord - 1, YCoord]);
                board.Tiles[XCoord - 1, YCoord].picked = true;
            }

            // W neighbour
            if (YCoord - 1 >= 0 && !board.Tiles[XCoord, YCoord - 1].exploded && !board.Tiles[XCoord, YCoord - 1].picked && 
                !board.Tiles[XCoord, YCoord - 1].Moving && board.Tiles[XCoord, YCoord - 1].Type == type)
            {
                similar_neighbours.Add(board.Tiles[XCoord, YCoord - 1]);
                board.Tiles[XCoord, YCoord - 1].picked = true;
            }

            // S neighbour
            if (XCoord + 1 < GameBoard.ROWS_NUMBER && !board.Tiles[XCoord + 1, YCoord].exploded && 
                !board.Tiles[XCoord + 1, YCoord].picked && !board.Tiles[XCoord + 1, YCoord].Moving && 
                board.Tiles[XCoord + 1, YCoord].Type == type)
            {
                similar_neighbours.Add(board.Tiles[XCoord + 1, YCoord]);
                board.Tiles[XCoord + 1, YCoord].picked = true;
            }

            // E neighbour
            if (YCoord + 1 < GameBoard.COLS_NUMBER && !board.Tiles[XCoord, YCoord + 1].exploded && !board.Tiles[XCoord, YCoord + 1].picked 
                && !board.Tiles[XCoord, YCoord + 1].Moving && board.Tiles[XCoord, YCoord + 1].Type == type)
            {
                similar_neighbours.Add(board.Tiles[XCoord, YCoord + 1]);
                board.Tiles[XCoord, YCoord + 1].picked = true;
            }
        }

        public void GravityCheck()
        {
            for (int i = GameBoard.ROWS_NUMBER - 1; i > XCoord; i--)
            {
                if (!board.Tiles[i, YCoord].refilled && !isMoving && !isFalling && !exploded)
                {
                    if (board.Tiles[i, YCoord].isFalling || board.Tiles[i, YCoord].exploded)
                    {
                        DropTo(new Vector2(GameBoard.NW_BOARD_COORD.X + GameBoard.CELL_DIM * YCoord,
                                GameBoard.NW_BOARD_COORD.Y + GameBoard.CELL_DIM * i), new Vector2(i, YCoord));

                        board.Tiles[i, YCoord].refilled = true;

                        System.Diagnostics.Debug.WriteLine("Gem [" + XCoord + "," + YCoord + "] go to [" + i + "," + YCoord + "]");

                        return;
                    }
                }
            }
        }

        public bool Moving { get { return isFalling || isMoving; } }

        public int Type { get { return type; } set { type = value; } }

        public int XCoord { get { return (int)pos_onboard.X; } }

        public int YCoord { get { return (int)pos_onboard.Y; } }

        public bool Picked { get { return picked; } set { picked = value; } }

        public bool Exploded { get { return exploded; } set { exploded = value; } }

        public bool IsFalling { get { return isFalling; } set { isFalling = value; } }

        public bool Refilled { get { return refilled; } set { refilled = value; } }

        public Vector2 PosOnBoard { get { return pos_onboard; } set { pos_onboard = value; } }

        public Vector2 DestinationOnBoard { get { return dest_pos_onboard; } set { dest_pos_onboard = value; } }
    }
}
